package com.ts.enterprise.exception.handle;

import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation;
import com.ts.enterprise.common.consts.CommonConstant;
import com.ts.enterprise.common.interceptor.RequestWrapper;
import com.ts.enterprise.common.response.ApiResult;
import com.ts.enterprise.common.utils.SpringContextUtil;
import com.ts.enterprise.enums.ApiCode;
import com.ts.enterprise.exception.BusinessException;
import com.ts.enterprise.exception.ExceptionEnum;
import com.ts.enterprise.exception.check.CheckException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.sql.SQLDataException;
import java.sql.SQLSyntaxErrorException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 当前微服务自定义异常拦截器
 * 对全局异常拦截进行补充  -> ${@link }
 *
 * @author meng.yu
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class GlobalExceptionHandler {

    /* ------------------ 请求校验拦截 start ------------------------------ */

    @Value("${system.isDebug}")
    private Boolean isDebug;

//    @Resource
//    private ErrorLogService errorLogService;

    /**
     * 处理服务器未知异常
     */
    @ExceptionHandler(Exception.class)
    public ApiResult handleException(Exception e) {
        log.error("未知异常: " + e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ExceptionEnum.UNKNOW_EXCEPTION.getCode(), ExceptionEnum.UNKNOW_EXCEPTION.getMsg());
    }

    /**
     * Valid校验参数的报错
     */
    @ExceptionHandler(value = BindException.class)
    public ApiResult handle(BindException e) {
        log.error(e.toString(), e);
        String errorParams = e.getFieldErrors().stream()
                .map(x -> x.getField() + ":" + x.getDefaultMessage())
                .collect(Collectors.joining(","));
        return ApiResult.failed(ApiCode.ERROR_PARAM.getCode(), ApiCode.ERROR_PARAM.getMessage() + errorParams,ApiCode.ERROR_PARAM.getLogicCode());
    }

    /**
     * 验证问题
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ApiResult handle(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        String errorMessages = e.getBindingResult().getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining(","));
        return ApiResult.failed(ApiCode.ERROR_PARAM.getCode(), ApiCode.ERROR_PARAM.getMessage() + errorMessages);
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    public ApiResult handle(ConstraintViolationException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.ERROR_PARAM.getCode(), ApiCode.ERROR_PARAM.getMessage() + e.getMessage());
    }
    /* ------------------ 请求校验拦截 end ------------------------------ */


    /* *******************自定义异常handler--start********************* */

    /**
     * 处理安全异常
     */
    @ExceptionHandler(SecurityException.class)
    public ApiResult handleException(SecurityException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ExceptionEnum.SECURITY_EXCEPTION.getCode(), e.getMessage());
    }

    @ExceptionHandler(CheckException.class)
    public ApiResult handleException(CheckException e) {
        log.error(e.getMessage(), e);
        return ApiResult.failed(e.getCode(), e.getMessage())
                .setCode(e.getCode());
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ApiResult handleException(BusinessException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(e.getCode(), e.getMessage(), e.getLogicCode());
    }

    /**
     * 请求缺少参数
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ApiResult handle(MissingServletRequestParameterException e) {
        log.error(e.getMessage(), e);
        return ApiResult.failed(ApiCode.MISSING_PARAM.getCode(),
                ApiCode.MISSING_PARAM.getMessage() + e.getParameterName());
    }

    /**
     * HTTP请求方式错误
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public ApiResult handle(HttpRequestMethodNotSupportedException e) {
        log.error(e.getMessage(), e);
        return ApiResult.failed(ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode(),
                ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getMessage() + e.getSupportedHttpMethods());
    }

    /**
     * 数据库执行错误
     */
    @ExceptionHandler(value = MysqlDataTruncation.class)
    public ApiResult handle(MysqlDataTruncation e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.DATABASE_EXECUTION_ERROR.getCode(),
                ApiCode.DATABASE_EXECUTION_ERROR.getMessage());
    }

    /**
     * 数据库查询结果重复
     */
    @ExceptionHandler(value = TooManyResultsException.class)
    public ApiResult handle(TooManyResultsException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.DATABASE_EXECUTION_ERROR.getCode(),"数据库查询结果重复");
    }

    /**
     * 数据库执行错误
     */
    @ExceptionHandler(value = SQLDataException.class)
    public ApiResult handle(SQLDataException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.DATABASE_EXECUTION_ERROR.getCode(),
                ApiCode.DATABASE_EXECUTION_ERROR.getMessage());
    }

    /**
     * 数据库查询字段异常
     */
    @ExceptionHandler(value = SQLSyntaxErrorException.class)
    public ApiResult handle(SQLSyntaxErrorException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.DATABASE_EXECUTION_ERROR.getCode(),"数据库查询字段异常");
    }

    /**
     * 数据库查询字段异常
     */
    @ExceptionHandler(value = BadSqlGrammarException.class)
    public ApiResult handle(BadSqlGrammarException e) {
        log.error(e.getMessage(), e);
        saveErrorLog(e.toString(), Arrays.toString(e.getStackTrace()));
        return ApiResult.failed(ApiCode.DATABASE_EXECUTION_ERROR.getCode(),"数据库操作异常");
    }

    private void saveErrorLog(String errorMessage, String errorInfo){
        if (StringUtils.isNotBlank(errorMessage)){
            try {
                List<String> notSaveErrorMsg = CommonConstant.notSaveErrorMsg();
                if (notSaveErrorMsg.contains(errorMessage)){
                    return;
                }
                HttpServletRequest request = SpringContextUtil.getRequest();
                String requestURI = request.getRequestURI();
                List<String> notSaveLogs = CommonConstant.notSaveLogUrl();
                for (String url : notSaveLogs) {
                    if (requestURI.contains(url)){
                        return;
                    }
                }
                RequestWrapper requestWrapper = new RequestWrapper(request);
//                errorLogService.saveError(requestURI, errorMessage, errorInfo, requestWrapper.getBody());
            }catch (Exception ignore){}
        }
    }

}
